//
// Created by Luhang on 2023/10/12/012.
//
//日志落地模块
//将格式化完成后的日志消息字符串输出到指定位置

#ifndef LOGPROJECT_SINK_H
#define LOGPROJECT_SINK_H

#include "util.hpp"
#include "message.hpp"
#include "formatter.hpp"
#include <memory>
#include <mutex>

namespace LuLog{
    class LogSink{
    public:
        using ptr = std::shared_ptr<LogSink>;
        LogSink(){};
        virtual ~LogSink(){};
        virtual void log(const char *data, size_t len) = 0;
    };

    //落地方向：标准输出
    class StdoutSink: public LogSink{
    public:
        using ptr = std::shared_ptr<StdoutSink>;
        StdoutSink() = default;
        void log(const char *data, size_t len) override{
            std::cout.write(data, len);
        }
    };

    //落地方向：指定文件
    class FileSink: public LogSink{
    public:
        using ptr = std::shared_ptr<FileSink>;
        FileSink(const std::string &fileName): _fileName(fileName){
            //创建日志文件所在目录
            util::File::createDirectory(util::File::path(fileName));
            //创建并打开文件
            _ofs.open(_fileName, std::ios::binary | std::ios::app);
            assert(_ofs.is_open());
        }

        const std::string &file(){
            return _fileName;
        }

        void log(const char *data, size_t len) override{
            _ofs.write((const char*)data, len);
            if(!_ofs.good())
                std::cout << "日志输出文件失败" << std::endl;
        }

    private:
        std::string _fileName;
        std::ofstream _ofs;
    };

    //落地方向：滚动文件
    class RollSink: public LogSink{
    public:
        using ptr = std::shared_ptr<RollSink>;
        RollSink(const std::string &baseName, size_t maxFSize):
                _baseName(baseName), _maxFSize(maxFSize), _curFSize(0), _nameCount(0){
            util::File::createDirectory(util::File::path(baseName));
        }
        void log(const char *data, size_t len) override{
            initLogFile();
            _ofs.write(data, len);
            if(!_ofs.good()){
                std::cout << "日志输出文件失败" << std::endl;
            }
            _curFSize += len;
        }
    private:
        void initLogFile(){
            if(!_ofs.is_open() || _curFSize >= _maxFSize){
                _ofs.close();
                std::string name = createFileName();
                _ofs.open(name, std::ios::binary | std::ios::app);
                assert(_ofs.is_open());
                _curFSize = 0;
                return;
            }
        }

        std::string createFileName(){
            time_t t = util::Date::now();
            struct tm lt{};
            localtime_r(&t, &lt);
            std::stringstream fileName;
            fileName << _baseName;
            fileName << lt.tm_year + 1900;
            fileName << lt.tm_mon + 1;
            fileName << lt.tm_mday;
            fileName << lt.tm_hour;
            fileName << lt.tm_min;
            fileName << lt.tm_sec;
            fileName << "-";
            fileName << _nameCount++;
            fileName << ".log";
            return fileName.str();
        }
        
    private:
        std::string _baseName;//基础文件名(实际输出文件名=基础文件名+扩展文件名【时间】)
        std::ofstream _ofs;
        size_t _maxFSize;//记录文件最大大小
        size_t _curFSize;//当前大小，避免每次都要获取文件属性
        size_t _nameCount;
    };

    class SinkFactory{
    public:
        template<typename SinkType, typename ...Args>
        static LogSink::ptr create(Args && ...args){
            return std::make_shared<SinkType>(std::forward<Args>(args)...);
        }
    };
}

#endif //LOGPROJECT_SINK_H
